یاد بگیرید چگونه از AbortController جاوا اسکریپت برای لغو مؤثر عملیات ناهمزمان مانند درخواستهای fetch، تایمرها و موارد دیگر استفاده کنید و کدی تمیزتر و با عملکرد بهتر داشته باشید.
AbortController در جاوا اسکریپت: تسلط بر لغو عملیات ناهمزمان
در توسعه وب مدرن، عملیات ناهمزمان همه جا هستند. واکشی داده از APIها، تنظیم تایمرها و مدیریت تعاملات کاربر اغلب شامل کدهایی است که به طور مستقل و بالقوه برای مدت زمان طولانی اجرا میشوند. با این حال، سناریوهایی وجود دارد که در آنها نیاز به لغو این عملیات قبل از تکمیل شدنشان دارید. اینجاست که رابط AbortController در جاوا اسکریپت به کمک میآید. این رابط یک روش تمیز و کارآمد برای ارسال سیگنال درخواست لغو به عملیات DOM و دیگر وظایف ناهمزمان فراهم میکند.
درک نیاز به لغو عملیات
قبل از پرداختن به جزئیات فنی، بیایید درک کنیم که چرا لغو عملیات ناهمزمان مهم است. این سناریوهای رایج را در نظر بگیرید:
- ناوبری کاربر: یک کاربر یک جستجو را آغاز میکند و یک درخواست API را فعال میکند. اگر کاربر قبل از تکمیل درخواست به سرعت به صفحه دیگری برود، درخواست اصلی دیگر اهمیتی ندارد و باید برای جلوگیری از ترافیک شبکه غیرضروری و عوارض جانبی احتمالی لغو شود.
- مدیریت تایماوت: شما برای یک عملیات ناهمزمان یک تایماوت تنظیم میکنید. اگر عملیات قبل از انقضای تایماوت تکمیل شود، باید تایماوت را لغو کنید تا از اجرای کد اضافی جلوگیری شود.
- جدا شدن کامپوننت (Unmounting): در فریمورکهای فرانتاند مانند React یا Vue.js، کامپوننتها اغلب درخواستهای ناهمزمان ارسال میکنند. هنگامی که یک کامپوننت جدا میشود (unmount)، هرگونه درخواست در حال انجام مرتبط با آن کامپوننت باید لغو شود تا از نشت حافظه و خطاهای ناشی از بهروزرسانی کامپوننتهای جدا شده جلوگیری شود.
- محدودیت منابع: در محیطهای با منابع محدود (مانند دستگاههای تلفن همراه، سیستمهای تعبیهشده)، لغو عملیات غیرضروری میتواند منابع ارزشمندی را آزاد کرده و عملکرد را بهبود بخشد. به عنوان مثال، لغو دانلود یک تصویر بزرگ اگر کاربر از آن بخش صفحه عبور کند.
معرفی AbortController و AbortSignal
رابط AbortController برای حل مشکل لغو عملیات ناهمزمان طراحی شده است. این رابط از دو جزء کلیدی تشکیل شده است:
- AbortController: این شیء سیگنال لغو را مدیریت میکند. این شیء یک متد واحد به نام
abort()دارد که برای ارسال سیگنال درخواست لغو استفاده میشود. - AbortSignal: این شیء سیگنالی را نشان میدهد که یک عملیات باید لغو شود. این سیگنال با یک
AbortControllerمرتبط است و به عملیات ناهمزمانی که نیاز به قابل لغو بودن دارد، ارسال میشود.
استفاده پایه: لغو درخواستهای Fetch
بیایید با یک مثال ساده از لغو یک درخواست fetch شروع کنیم:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});
// To cancel the fetch request:
controller.abort();
توضیح:
- ما یک نمونه
AbortControllerایجاد میکنیم. - ما
AbortSignalمرتبط را ازcontrollerدریافت میکنیم. - ما
signalرا به گزینههایfetchارسال میکنیم. - اگر نیاز به لغو درخواست داشته باشیم،
controller.abort()را فراخوانی میکنیم. - در بلوک
.catch()، ما بررسی میکنیم که آیا خطا از نوعAbortErrorاست یا خیر. اگر چنین باشد، میدانیم که درخواست لغو شده است.
مدیریت AbortError
هنگامی که controller.abort() فراخوانی میشود، درخواست fetch با یک AbortError رد (reject) خواهد شد. مدیریت صحیح این خطا در کد شما بسیار مهم است. عدم انجام این کار میتواند منجر به رد شدن پرامیسهای مدیریت نشده و رفتار غیرمنتظره شود.
در اینجا یک مثال قویتر با مدیریت خطا آورده شده است:
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
return null; // Or throw the error to be handled further up
} else {
console.error('Fetch error:', error);
throw error; // Re-throw the error to be handled further up
}
}
}
fetchData();
// To cancel the fetch request:
controller.abort();
بهترین روشها برای مدیریت AbortError:
- بررسی نام خطا: همیشه بررسی کنید که
error.name === 'AbortError'باشد تا مطمئن شوید که نوع خطای صحیحی را مدیریت میکنید. - بازگرداندن یک مقدار پیشفرض یا پرتاب مجدد خطا: بسته به منطق برنامه شما، ممکن است بخواهید یک مقدار پیشفرض (مانند
null) بازگردانید یا خطا را مجدداً پرتاب کنید تا در سطوح بالاتر پشته فراخوانی مدیریت شود. - پاکسازی منابع: اگر عملیات ناهمزمان منابعی (مانند تایمرها، شنوندگان رویداد) را تخصیص داده است، آنها را در کنترلکننده
AbortErrorپاکسازی کنید.
لغو تایمرها با AbortSignal
از AbortSignal همچنین میتوان برای لغو تایمرهای ایجاد شده با setTimeout یا setInterval استفاده کرد. این کار به کمی تلاش دستی بیشتری نیاز دارد، زیرا توابع تایمر داخلی به طور مستقیم از AbortSignal پشتیبانی نمیکنند. شما باید یک تابع سفارشی ایجاد کنید که به سیگنال لغو گوش دهد و هنگام فعال شدن آن، تایمر را پاک کند.
function cancellableTimeout(callback, delay, signal) {
let timeoutId;
const timeoutPromise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
resolve(callback());
}, delay);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new Error('Timeout Aborted'));
});
});
return timeoutPromise;
}
const controller = new AbortController();
const signal = controller.signal;
cancellableTimeout(() => {
console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));
// To cancel the timeout:
controller.abort();
توضیح:
- تابع
cancellableTimeoutیک تابع بازگشتی (callback)، یک تأخیر (delay) و یکAbortSignalرا به عنوان آرگومان دریافت میکند. - این تابع یک
setTimeoutرا تنظیم میکند و شناسه تایماوت را ذخیره میکند. - این تابع یک شنونده رویداد به
AbortSignalاضافه میکند که به رویدادabortگوش میدهد. - هنگامی که رویداد
abortفعال میشود، شنونده رویداد تایمر را پاک میکند و پرامیس را رد (reject) میکند.
لغو شنوندگان رویداد (Event Listeners)
مشابه تایمرها، میتوانید از AbortSignal برای لغو شنوندگان رویداد استفاده کنید. این کار به ویژه زمانی مفید است که میخواهید شنوندگان رویداد مرتبط با کامپوننتی که در حال جدا شدن (unmount) است را حذف کنید.
const controller = new AbortController();
const signal = controller.signal;
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
}, { signal });
// To cancel the event listener:
controller.abort();
توضیح:
- ما
signalرا به عنوان یک گزینه به متدaddEventListenerارسال میکنیم. - هنگامی که
controller.abort()فراخوانی میشود، شنونده رویداد به طور خودکار حذف خواهد شد.
AbortController در کامپوننتهای React
در React، میتوانید از AbortController برای لغو عملیات ناهمزمان هنگام جدا شدن یک کامپوننت استفاده کنید. این کار برای جلوگیری از نشت حافظه و خطاهای ناشی از بهروزرسانی کامپوننتهای جدا شده ضروری است. در اینجا یک مثال با استفاده از هوک useEffect آورده شده است:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setData(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
}
}
fetchData();
return () => {
controller.abort(); // Cancel the fetch request when the component unmounts
};
}, []); // Empty dependency array ensures this effect runs only once on mount
return (
{data ? (
Data: {JSON.stringify(data)}
) : (
Loading...
)}
);
}
export default MyComponent;
توضیح:
- ما یک
AbortControllerرا در داخل هوکuseEffectایجاد میکنیم. - ما
signalرا به درخواستfetchارسال میکنیم. - ما یک تابع پاکسازی از هوک
useEffectبازمیگردانیم. این تابع هنگام جدا شدن کامپوننت فراخوانی خواهد شد. - در داخل تابع پاکسازی، ما
controller.abort()را برای لغو درخواست fetch فراخوانی میکنیم.
موارد استفاده پیشرفته
زنجیرهسازی AbortSignalها
گاهی اوقات، ممکن است بخواهید چندین AbortSignal را به هم زنجیر کنید. به عنوان مثال، ممکن است یک کامپوننت والد داشته باشید که نیاز به لغو عملیات در کامپوننتهای فرزند خود دارد. شما میتوانید با ایجاد یک AbortController جدید و ارسال سیگنال آن به هر دو کامپوننت والد و فرزند به این هدف دست یابید.
استفاده از AbortController با کتابخانههای شخص ثالث
اگر از کتابخانه شخص ثالثی استفاده میکنید که به طور مستقیم از AbortSignal پشتیبانی نمیکند، ممکن است نیاز داشته باشید کد خود را با مکانیزم لغو آن کتابخانه تطبیق دهید. این کار ممکن است شامل پیچیدن توابع ناهمزمان کتابخانه در توابع خودتان باشد که AbortSignal را مدیریت میکنند.
مزایای استفاده از AbortController
- بهبود عملکرد: لغو عملیات غیرضروری میتواند ترافیک شبکه، استفاده از CPU و مصرف حافظه را کاهش دهد و منجر به بهبود عملکرد، به ویژه در دستگاههای با منابع محدود شود.
- کد تمیزتر:
AbortControllerیک روش استاندارد و زیبا برای مدیریت لغو فراهم میکند و کد شما را خواناتر و قابل نگهداریتر میسازد. - جلوگیری از نشت حافظه: لغو عملیات ناهمزمان مرتبط با کامپوننتهای جدا شده از نشت حافظه و خطاهای ناشی از بهروزرسانی کامپوننتهای جدا شده جلوگیری میکند.
- تجربه کاربری بهتر: لغو درخواستهای نامربوط میتواند با جلوگیری از نمایش اطلاعات منسوخ و کاهش تأخیر درک شده، تجربه کاربری را بهبود بخشد.
سازگاری با مرورگرها
AbortController به طور گسترده در مرورگرهای مدرن از جمله Chrome، Firefox، Safari و Edge پشتیبانی میشود. میتوانید برای آخرین اطلاعات، جدول سازگاری را در MDN Web Docs بررسی کنید.
پلیفیلها (Polyfills)
برای مرورگرهای قدیمیتری که به طور بومی از AbortController پشتیبانی نمیکنند، میتوانید از یک پلیفیل استفاده کنید. پلیفیل قطعه کدی است که عملکرد یک ویژگی جدیدتر را در مرورگرهای قدیمیتر فراهم میکند. چندین پلیفیل AbortController به صورت آنلاین در دسترس هستند.
نتیجهگیری
رابط AbortController ابزاری قدرتمند برای مدیریت عملیات ناهمزمان در جاوا اسکریپت است. با استفاده از AbortController، میتوانید کدی تمیزتر، با عملکرد بهتر و قویتر بنویسید که لغو عملیات را به خوبی مدیریت میکند. چه در حال واکشی داده از APIها باشید، چه در حال تنظیم تایمرها یا مدیریت شنوندگان رویداد، AbortController میتواند به شما در بهبود کیفیت کلی برنامههای وب خود کمک کند.